using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace DailyTestBLL.XMLParse
{
    public class XMLParse
    {
        #region fields

        //all xml text
        private string m_XmlText;
        //xml text left
        private string m_XmlLeft;
        //current char
        private char m_chXmlCurrent;

        private int i_CurrentIndex;

        private int i_LeftLength;

        private int i_AllLength;

        private Stack<XMLTag> tagStack = new Stack<XMLTag>();

        private Hashtable htTags = new Hashtable();

        #endregion

        #region properties

        #endregion

        public XMLParse( string XmlText )
        {
            this.m_XmlText = XmlText;
            this.m_XmlLeft = XmlText;
            this.i_AllLength = m_XmlText.Length;
            this.i_LeftLength = m_XmlLeft.Length;
            this.i_CurrentIndex = 0;
        }

        public void Prase()
        {
            int iTagBeginPos = 0;
            int iTagEndPos = 0;
            int iTagInternalPos1 = 0;
            int iTagInternalPos2 = 0;
            StringBuilder sbTmp = new StringBuilder();

            while (m_XmlLeft.Length > 0)
            {
                char ch = GetCurrentChar();

                if (ch == '<')
                {
                    ch = MoveNext();
                    if (((ch > 'A') && (ch < 'Z')) || ((ch > 'a') && (ch < 'z')) || ch == '!')
                    {
                        iTagBeginPos = i_CurrentIndex - 2;
                        XMLTag tag = GetTag();
                        if (tag != null)
                        {
                            tag.BeginPos = iTagBeginPos;
                            if (i_LeftLength > 0)
                            {
                                string CurrentText = GetCurrentText();
                                tagStack.Push(new XMLTextTag(CurrentText));
                            }
                            tagStack.Push(tag);
                        }
                    }

                    if (ch == '/')
                    {
                        iTagInternalPos2 = i_CurrentIndex;

                        string tagName = GetTagName();

                        iTagEndPos = i_CurrentIndex;

                        XMLTag stackTag = tagStack.Peek();
                        if (stackTag.TagName == tagName)
                        {
                            int iLevel = tagStack.Count;
                            if (htTags.Contains(iLevel))
                            {
                                IList lstHtTag = htTags[iLevel] as IList;
                                XMLTag popTag = tagStack.Pop();
                                popTag.EndPos = i_CurrentIndex;
                                popTag.InternalPos2 = iTagInternalPos2;
                                popTag.EndPos = iTagEndPos;
                                lstHtTag.Add(popTag);
                                htTags[iLevel] = lstHtTag;
                            }
                            else
                            {
                                IList lstHtTag = new ArrayList();
                                XMLTag popTag = tagStack.Pop();
                                popTag.EndPos = i_CurrentIndex;
                                popTag.InternalPos2 = iTagInternalPos2;
                                popTag.EndPos = iTagEndPos;
                                lstHtTag.Add(popTag);
                                htTags.Add(iLevel, lstHtTag);
                            }
                        }
                    }
                }
                else 
                {
                    sbTmp.Append(ch);
                }
                ch = MoveNext();
            }
        }

        private char GetCurrentChar()
        {
            if (i_LeftLength > 0)
                return m_XmlLeft[0];
            return '>';
        }

        private char MoveNext()
        {
            m_XmlLeft = m_XmlLeft.Substring(1);
            i_CurrentIndex++;
            i_LeftLength--;
            return GetCurrentChar();
        }

        /// <summary>
        /// per get number of count chars,dont change the currentindex and the length
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        private char[] PerGetChar(int count)
        {
            if (count > 0)
            {
                char[] perGets = new char[count];
                for (int i = 0; i < count; i++)
                {
                    perGets[i] = m_XmlLeft[i];
                }
                return perGets;
            }
            return new char[1];
        }

        private string GetCurrentText()
        {
            char ch = GetCurrentChar();
            StringBuilder sbContext = new StringBuilder();
            while( ch != '<'  )
            {
                sbContext.Append(ch);
                ch = MoveNext();
            }
            return sbContext.ToString();
        }

        private XMLTag GetTag()
        {
            try
            {
                XMLTag aTag = new XMLTag();
                StringBuilder sbTagInfo = new StringBuilder();
                IList lstTagInfo = new ArrayList();
                char ch = GetCurrentChar();

                while (ch != '>')
                {
                    if (ch == '/')
                    {
                        char[] chs = PerGetChar(1);
                        if (chs[0] == '>')
                        {
                            aTag.TagContext = "";
                            aTag.InternalPos1 = i_CurrentIndex + 1;
                            aTag.InternalPos2 = i_CurrentIndex + 1;
                            aTag.EndPos = i_CurrentIndex + 1;
                        }
                    }
                    else
                    {
                        while (ch == ' ')
                        {
                            int iBlank = 0;
                            char[] chAfterBlank = PerGetChar(iBlank + 1);
                            while (chAfterBlank[iBlank] == ' ')
                            {
                                chAfterBlank = PerGetChar(iBlank+2);
                                iBlank++;
                            }
                            for (int i = 0; i < iBlank - 1; i++)
                            {
                                MoveNext();
                            }
                            if (chAfterBlank[0] == '=')
                            {
                                
                                int iBlank2 = 0;
                                chAfterBlank = PerGetChar(iBlank2 + 1);
                                while (chAfterBlank[iBlank2] == ' ')
                                {
                                    chAfterBlank = PerGetChar(iBlank2+2);
                                    iBlank2++;
                                }
                                for (int i = 0; i < iBlank2 - 1; i++)
                                {
                                    MoveNext();
                                }
                            }
                            else
                            {
                                lstTagInfo.Add(sbTagInfo.ToString());
                                sbTagInfo.Remove(0, sbTagInfo.Length);
                                break;
                            }
                        }
                        sbTagInfo.Append(ch);
                    }
                    ch = MoveNext();
                }
                MoveNext();
                aTag.InternalPos1 = i_CurrentIndex;

                aTag.TagName = lstTagInfo[0] as string;
                foreach (string s in lstTagInfo)
                {
                    string[] sSplit = s.Split('=');
                    if (sSplit.Length == 2)
                    {
                        aTag.AddAttriute(sSplit[0].Trim(), sSplit[1].Trim());
                    }
                }
            }
            catch 
            {
                return null;
            }
            return new XMLTag();
        }

        private string GetTagName()
        {
            char ch = GetCurrentChar();
            StringBuilder sbTagName = new StringBuilder();
            while (ch != '>')
            {
                sbTagName.Append(ch);
                ch = MoveNext();
            }
            return sbTagName.ToString();
        }

    }
}
